import { get } from 'lodash-es'

declare type ObjectRecord = Record<string, any>

/*
RowSpanningData: {
  [rowIndex]: {
    [field]: [rowSpan]
  }
}
*  */
export type RowSpanningData = Record<string, Record<string, number>>

/*
* 值比较 选项
*
* 同为 null | undefined | 空字符串 是否视为相同
* */
export interface CompareOptions {
  null?: boolean
  undefined?: boolean
  emptyString?: boolean
}

// 值比较 - 相同时返回 true
function compareValue(prevValue: any, value: any, options?: CompareOptions): boolean {
  // 不相等
  if (prevValue !== value) {
    return false
  }

  // 以下均为 值相等 情况

  // 未配置 options 时 需要判断值是否为以下情况
  if (!options) {
    // 值为 null、undefined、空字符串 的 返回 false
    return value !== null
      && typeof value !== 'undefined'
      && value !== ''
  }

  // 不属于 特殊情况
  if (![null, undefined, ''].includes(value)) {
    return true
  }

  const allowNull = get(options, 'null', false)
  const allowUndefined = get(options, 'undefined', false)
  const allowEmptyString = get(options, 'emptyString', false)

  return (allowNull && value === null)
    || (allowUndefined && typeof value === undefined)
    || (allowEmptyString && value === '')
}

/*
* 合并 表格 行
*
* 默认逻辑：当 数据相同时 合并
*
*  */
export default function gridRowSpanning(
  // 数据
  data: ObjectRecord[],
  // 需要 合并的 字段 列表
  fields: string[],
  // 比较配置
  options?: CompareOptions
): RowSpanningData {
  const res: RowSpanningData = {}
  const map: any = {}

  fields.forEach(field => {
    map[field] = {
      prevValue: null, // 上一行 值
      rowSpan: 1 // 跨行数
    }
  })

  // 避免最后一项数据 无法处理
  data = [...data, {}]

  data.forEach((item, index) => {
    fields.forEach(field => {
      const record = map[field]
      const value = item[field]

      // 判断 当前值与上一行值 是否相同
      const isSameValue = compareValue(record.prevValue, value, options)

      // 单元格有值，且 当前行与上一行数据相等 - 跨行数 +1
      if (isSameValue) {
        record.rowSpan++
      } else {
        // 不相等时 - 跨行数 匹配结束
        if (record.rowSpan > 1) {
          // 跨行数 大于 1，则需要记录
          const obj = { [field]: record.rowSpan }

          const targetIndex = index - record.rowSpan
          const resItem = res[targetIndex]

          res[targetIndex] = resItem
            ? Object.assign(resItem, obj)
            : obj
        }

        // 重置 参数 - 进入 下一次 匹配
        record.prevValue = value
        record.rowSpan = 1
      }
    })
  })

  return res
}